﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web;
using System.Collections.Specialized;
using System.Web.Helpers;
using Mindfor.Web.Routing;

namespace Mindfor.Web
{
	/// <summary>
	/// Provides extension Post-Redirect-Get functions for Controller.
	/// Provides extensions functions for ControllerDescriptor and ActionDescriptor.
	/// </summary>
	public static class ControllerExtensions
	{
		public static readonly string FormKey = "Form";
		public static readonly string ModelStateKey = "ModelState";

		/// <summary>
		/// Saves model state to TempData.
		/// </summary>
		public static void SaveModelState(this ControllerBase controller)
		{
			controller.TempData[FormKey] = controller.ControllerContext.HttpContext.Request.Form;
			controller.TempData[ModelStateKey] = controller.ViewData.ModelState;
		}

		/// <summary>
		/// Loads model state from TempData.
		/// </summary>
		public static void LoadModelState(this ControllerBase controller)
		{
			NameValueCollection form = controller.TempData[FormKey] as NameValueCollection;
			if (form != null)
			{
				foreach (string key in form.Keys)
					controller.ViewData[key] = form[key];
			}

			ModelStateDictionary dic = controller.TempData[ModelStateKey] as ModelStateDictionary;
			if (dic != null)
			{
				foreach (var kv in dic)
				{
					if (!controller.ViewData.ModelState.ContainsKey(kv.Key))
						controller.ViewData.ModelState.Add(kv);
				}
			}
		}

		/// <summary>
		/// Retrieves custom attributes of a specified type that are defined for this member, excluding named attributes.
		/// </summary>
		/// <typeparam name="T">The type of the custom attributes.</typeparam>
		/// <param name="controller">Controller where to search.</param>
		/// <param name="inherit"> true to look up the hierarchy chain for the inherited custom attribute; otherwise, false.</param>
		/// <returns>An array of custom attributes, or an empty array if no custom attributes exist.</returns>
		public static T[] GetCustomAttributes<T>(this ControllerDescriptor controller, bool inherit)
		{
			object[] attrs = controller.GetCustomAttributes(typeof(T), inherit);
			T[] res = new T[attrs.Length];
			for (int i=0; i<attrs.Length; i++)
				res[i] = (T)attrs[i];
			return res;
		}

		/// <summary>
		/// Retrieves custom attribute of a specified type that are defined for this member, excluding named attributes.
		/// </summary>
		/// <typeparam name="T">The type of the custom attributes.</typeparam>
		/// <param name="controller">Controller where to search.</param>
		/// <param name="inherit"> true to look up the hierarchy chain for the inherited custom attribute; otherwise, false.</param>
		/// <returns>First custom attribute, or null if no custom attribute exist.</returns>
		public static T GetCustomAttribute<T>(this ControllerDescriptor controller, bool inherit)
		{
			object[] attrs = controller.GetCustomAttributes(typeof(T), inherit);
			return attrs.Length > 0 ? (T)attrs[0] : default(T);
		}

		/// <summary>
		/// Retrieves custom attributes of a specified type that are defined for this member, excluding named attributes.
		/// </summary>
		/// <typeparam name="T">The type of the custom attributes.</typeparam>
		/// <param name="action">Action where to search.</param>
		/// <param name="inherit"> true to look up the hierarchy chain for the inherited custom attribute; otherwise, false.</param>
		/// <returns>An array of custom attributes, or an empty array if no custom attributes exist.</returns>
		public static T[] GetCustomAttributes<T>(this ActionDescriptor action, bool inherit)
		{
			object[] attrs = action.GetCustomAttributes(typeof(T), inherit);
			T[] res = new T[attrs.Length];
			for (int i=0; i<attrs.Length; i++)
				res[i] = (T)attrs[i];
			return res;
		}

		/// <summary>
		/// Retrieves custom attribute of a specified type that are defined for this member, excluding named attributes.
		/// </summary>
		/// <typeparam name="T">The type of the custom attributes.</typeparam>
		/// <param name="action">Action where to search.</param>
		/// <param name="inherit"> true to look up the hierarchy chain for the inherited custom attribute; otherwise, false.</param>
		/// <returns>First custom attribute, or null if no custom attribute exist.</returns>
		public static T GetCustomAttribute<T>(this ActionDescriptor action, bool inherit)
		{
			object[] attrs = action.GetCustomAttributes(typeof(T), inherit);
			return attrs.Length > 0 ? (T)attrs[0] : default(T);
		}

		/// <summary>
		/// Retrieves a list of action-method descriptors with specified action name.
		/// </summary>
		/// <param name="controller">Controller.</param>
		/// <param name="actionName">Action to search.</param>
		public static ActionDescriptor[] GetCanonicalAction(this ControllerDescriptor controller, string actionName)
		{
			List<ActionDescriptor> actions = new List<ActionDescriptor>();
			foreach (ActionDescriptor a in controller.GetCanonicalActions())
			{
				if (a.ActionName == actionName)
					actions.Add(a);
			}
			return actions.ToArray();
		}

		/// <summary>
		/// Retrieves a list of action-method descriptors ny main action name.
		/// Main action name is looked up in UrlSubAtionAttribute.
		/// </summary>
		/// <param name="controller">Controller.</param>
		/// <param name="mainAction">Main action name to search.
		/// If null, then returns with empty UrlSubAction attribute or with out attribute.</param>
		/// <returns></returns>
		public static ActionDescriptor[] GetCanonicalActionsByMainAction(this ControllerDescriptor controller, string mainAction)
		{
			List<ActionDescriptor> actions = new List<ActionDescriptor>();
			foreach (ActionDescriptor a in controller.GetCanonicalActions())
			{
				UrlSubActionAttribute attr = a.GetCustomAttributes<UrlSubActionAttribute>(true).FirstOrDefault();
				bool match1 = String.IsNullOrEmpty(mainAction) && (attr == null || String.IsNullOrEmpty(attr.MainAction));
				bool match2 = attr != null && mainAction == attr.MainAction;
				if (match1 || match2)
					actions.Add(a);
			}

			return actions.ToArray();
		}

		/// <summary>
		/// Returns CmsAuthorizeAttribute that is applied to action or its controller.
		/// </summary>
		/// <param name="action">Action to get attribute for.</param>
		public static CmsAuthorizeAttribute GetCmsAuthorizeAttribute(this ActionDescriptor action)
		{
			if (action == null)
				return null;

			// search action
			CmsAuthorizeAttribute[] attrs = action.GetCustomAttributes<CmsAuthorizeAttribute>(true);

			// search controller
			if (attrs.Length == 0)
				attrs = action.ControllerDescriptor.GetCustomAttributes<CmsAuthorizeAttribute>(true);

			// return result;
			return attrs.FirstOrDefault();
		}
	}
}
